package org.evil.dog.gateway.filter;


import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.evil.dog.gateway.feign.ManagerLoginFeign;
import org.evil.dog.gateway.feign.dto.ManagerSessionDto;
import org.evil.dog.gateway.utils.ZMDateTimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;
import java.util.Date;

/**
 * @Author: 阿巳的狗
 * @Des: 网关错误处理类
 * @Date: 2021-04-30
 */
@Log4j2
@Component
public class UserOneLoginCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<UserOneLoginCheckGatewayFilterFactory.Config> {
    private final String ZOOM_AUTH = "zoom-auth";

    private final String APP_USER_ID = "userId";

    private final String TOKEN_ZONE = "token_zone";
    private final String TOKEN_IAT_TIME = "tokenIatTime";
    private final String SYS_NO_LOGIN_CHECK = "sys_no_login_check";


    @Autowired
    private ManagerLoginFeign loginFeign;
    @Autowired
    private LoadBalancerClient loadBalancer;

    public UserOneLoginCheckGatewayFilterFactory() {
        super(Config.class);
        log.info("网关-加载网关用户鉴权过滤器");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return new AuthTokenCheckGateWayFilter(config, loadBalancer);
    }

    @Data
    public static class Config {

    }

    public class AuthTokenCheckGateWayFilter extends DogGatewayFilter implements GatewayFilter, Ordered {
        private Config config;

        public AuthTokenCheckGateWayFilter(Config config, LoadBalancerClient loadBalancer) {
            this.config = config;
            this.loadBalancer = loadBalancer;
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

            //如果用户验证不需,要则这里也不需要去检测是否需要一人登录检测
            String sys_no_login_check = exchange.getAttribute(SYS_NO_LOGIN_CHECK);
            if (sys_no_login_check != null && SYS_NO_LOGIN_CHECK.equals(sys_no_login_check)) {
                return chain.filter(exchange);
            }

            String tokenIatTime = exchange.getAttribute(TOKEN_IAT_TIME);
            String userId = exchange.getAttribute(APP_USER_ID);
            String token_zone = exchange.getAttribute(TOKEN_ZONE);
            try {
                Long uid = Long.valueOf(userId);
                Long iatTime = Long.parseLong(tokenIatTime);
                //开启应用只有一个人登录
                Mono<Void> mono = doOnlyUserLoginOut(exchange, uid, iatTime, token_zone);
                if (mono != null) {
                    return mono;
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }

            return chain.filter(exchange);
        }

        private Mono<Void> doOnlyUserLoginOut(ServerWebExchange exchange, Long uid, Long iatTime, String token_zone) {
            try {
                ManagerSessionDto user = loginFeign.session(uid);
                if (user != null && user.getStatus() != null && user.getStatus() == 0) {
                    String msg = "您的账号已经被禁用，如有疑问请联系管理员。";
                    String info = java.net.URLEncoder.encode(msg, "UTF-8")
                            .replace("+", "%20");

                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.OK);
                    String resBody = "{}";
                    HttpHeaders httpHeaders = response.getHeaders();
                    httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
                    httpHeaders.add(ZOOM_AUTH, info);
                    DataBuffer bodyDataBuffer = response.bufferFactory()
                            .wrap(resBody.getBytes(Charset.forName("utf-8")));
                    return response.writeWith(Mono.just(bodyDataBuffer));
                }
                Long newIat = user.getIat();
                Long tokenIat = iatTime;
                if (tokenIat < newIat) {
                    String zone = token_zone != null ? token_zone : "28800";
                    String time = "";
                    try {
                        int offset = Integer.valueOf(zone);
                        time = ZMDateTimeUtil.formMatter(new Date(newIat), offset);
                    } catch (Exception e) {
                        log.warn("网关-时区格式错误：zone={}", zone);
                        time = ZMDateTimeUtil.formMatter(new Date(newIat), zone);
                    }
                    String msg = "%s在其他地方登录。请注意帐号安全，如果这不是您的操作，您的密码很有可能泄漏，请及时更改密码。";
                    String info = java.net.URLEncoder.encode(String.format(msg, time), "UTF-8")
                            .replace("+", "%20");

                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.OK);
                    String resBody = "{}";
                    HttpHeaders httpHeaders = response.getHeaders();
                    httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
                    httpHeaders.add(ZOOM_AUTH, info);
                    DataBuffer bodyDataBuffer = response.bufferFactory()
                            .wrap(resBody.getBytes(Charset.forName("utf-8")));
                    return response.writeWith(Mono.just(bodyDataBuffer));
                }
            } catch (Exception e) {
                log.info("网关-一个人登录验证失败:", e);
            }
            return null;
        }

        @Override
        public int getOrder() {
            return 1;
        }

    }
}
